<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>QeePHP 快速入门</title>
<link href="css/base.css" rel="stylesheet" type="text/css">
</head>
<body>

<div id="page">


<div class="guide-section">

  <div class="guide-header">
    <span class="nav">
      <a href="http://qeephp.com/docs/">文档索引</a>
      &raquo;
      <a href="index.html">QeePHP 快速入门</a>
      &raquo;
      <a href="node-routes.html">美化 URL</a>
      &raquo;
      添加路由规则    </span>
  </div>

  <div class="guide-section-details formatted">

    
<h1>添加路由规则</h1>

<p>打开文件 configs/routes.yaml
文件，可以看到目前的默认路由规则：</p>

<pre class="python code">_default_:
  pattern: /:controller/:action/<span
class="sy0">*</span>
  defaults:
    controller: default
    action: index</pre>

<h2>路由的正向解析</h2>

<p>上面的规则中，“_default_”是规则的名称，每一条规则必须有一个唯一的名称。</p>

<p>pattern 表示该规则是一个基于模式的规则，如果是 regx
则是基于正则的规则。同一条规则只能两者之中选一种，不同规则可以不同的类型。defaults
则指定各个变量的默认值。</p>

<blockquote>
	<p>基于正则的路由适合非常复杂的情况。限于篇幅这里我们只大概说明一下基于模式的路由规则。</p>
</blockquote>

<p>pattern 的值就是要解析或最终生成的 URL 地址样式。这个 URL
用“/”符号分割为多个部分。而每一个以“:”开头的部分都是一个变量。例如“/:controller/:action/*”这个模式就由三个部分，其中前两个部分都是变量，而最后的则是通配符（关于通配符稍后再详细说明）。</p>

<p>现在来看看这个模式怎么用于正向解析。</p>

<ul>
	<li><strong>"/projects"</strong>
		<p>解析时，首先用“/”将 URL
		地址分割为多个部分，然后与分割后的模式依次比对。</p>

		<ol>
			<li>模式的第一部分是 :controller
			变量。而这个变量是一个内置变量，用于匹配的正则表达式是“([a-z][a-z0-9_]*)*”，也就是说必须是“以字母开头，并且由字母、数字和下划线组成”。由于
			project 完全符合这个要求，因此比对成功。</li>

			<li>模式的第二部分是 :action 变量。:action 与 :controller
			一样都是内置变量，比对规则也一样。但显然要比对的 URL
			中已经没有第二部分了。但是我们看前面写的路由规则中已经通过
			defaults 为 action 变量指定了默认值。所以比对成功。</li>

			<li>模式的第三部分是“*”通配符。“*”通配符可以匹配任意数量成对出现的参数，由于
			URL
			中并没有多余参数，最终该路由规则的三个部分都比对成功。</li>

			<li>比对成功后，projects 将会作为 :controller 变量的值，而
			:action 变量使用了默认值。因此这个 URL 最后会被导向 projects
			控制器的 index 动作。</li>
		</ol>
	</li>
</ul>

<ul>
	<li><strong>"/docs/show/id/2"</strong>
		<p>这个 URL 分割后有 4
		个部分，按照前面的过程，前两个部分都会比对成功。而通配符则比对成功最后的“id”和“2”两个部分，最终将
		URL 导向 docs 控制器的 show 动作，并且附带参数 id=2。</p>
	</li>
</ul>

<p>再看一个复杂一点的例子：</p>

<pre class="python code">projects_download:
  pattern: <span
class="st0">&quot;/projects/:name/download/:file&quot;</span>
  defaults:
    controller: <span
class="st0">&quot;projects&quot;</span>
    action: <span
class="st0">&quot;download&quot;</span>
  config:
    name: <span
class="st0">&quot;[a-z0-9]+&quot;</span>
    <span
class="kw2">file</span>: <span class="st0">&quot;[0-9]+&quot;</span></pre>

<ul>
	<li><strong>"/projects/qeephp/download/2"</strong>
		<p>这个 URL 一共四个部分，依次比对的结果是：</p>

		<ol>
			<li>模式的第一部分不是变量，则以字符串方式和 URL
			相同位置的部分进行比较。project 与 project
			相同，因此比对通过。</li>

			<li>模式的第二部分是 :name
			变量。由于这个变量不是内置变量，因此需要通过 configs
			来指定比对规则。:name
			的比对规则是“[a-z0-9]+”（也是一个正则），和 qeephp
			相比对，结果通过。</li>

			<li>模式第三部分还是字符串，同样比对通过。</li>

			<li>与 :name 变量类似，“2”完全符合 :file
			变量的比对规则。至此比对全部通过，最终 URL 会被导向到
			projects 控制器的 download 动作（两者都通过 defaults
			指定），并且附带参数 name=qeephp 和 file=2。</li>
		</ol>
	</li>
</ul>

<p>在比对过程中，必须所有部分都比对成功才算通过，否则一律算作比对失败。一条规则比对失败，就会用下一条规则来比对，直到找到可以匹配的规则。如果所有规则都失败，那么会按照
/key/value 形式把 URL 转换为参数对。</p>

<p>所以匹配范围更低的规则应该放在更前面。这样才能确保不会因为一个规则比另一个规则具有更大的匹配范围，而把匹配范围更小那个规则忽略了。</p>

<h2>路由的反向解析</h2>

<p>与正向解析相比，反向解析复杂得多。因为反向解析是用参数来比对的，而不是
URL 地址。</p>

<pre class="php code"><span class="kw3">array</span><span
class="br0">&#40;</span>
    <span class="st_h">'controller'</span> <span
class="sy0">=&gt;</span> <span class="st_h">'projects'</span><span
class="sy0">,</span>
    <span class="st_h">'action'</span>     <span
class="sy0">=&gt;</span> <span class="st_h">'download'</span><span
class="sy0">,</span>
    <span class="st_h">'name'</span>       <span
class="sy0">=&gt;</span> <span class="st_h">'qeephp'</span><span
class="sy0">,</span>
    <span class="st_h">'file'</span>       <span
class="sy0">=&gt;</span> <span class="nu0">2</span><span
class="sy0">,</span>
<span class="br0">&#41;</span></pre>

<p>上面是生成 URL 的参数，用于匹配的规则有：</p>

<pre class="ini code">docs_page:
  pattern: <span
class="st0">&quot;/docs/:name/:file&quot;</span>
  defaults:
    controller: <span
class="st0">&quot;docs&quot;</span>
    action: <span
class="st0">&quot;page&quot;</span>
    file: <span
class="st0">&quot;index&quot;</span>
  config:
    name: <span
class="st0">&quot;[a-z0-9\-]+&quot;</span>
    file: <span
class="st0">&quot;[a-z0-9\-]+&quot;</span>
&nbsp;
projects_show:
  pattern: <span
class="st0">&quot;/projects/:name&quot;</span>
  defaults:
    controller: <span
class="st0">&quot;projects&quot;</span>
    action: <span
class="st0">&quot;show&quot;</span>
  config:
    name: <span
class="st0">&quot;[a-z0-9]+&quot;</span>
&nbsp;
projects_download_all:
  pattern: <span
class="st0">&quot;/projects/:name/download&quot;</span>
  defaults:
    controller: <span
class="st0">&quot;projects&quot;</span>
    action: <span
class="st0">&quot;download&quot;</span>
  config:
    name: <span
class="st0">&quot;[a-z0-9]+&quot;</span>
&nbsp;
projects_download:
  pattern: <span
class="st0">&quot;/projects/:name/download/:file&quot;</span>
  defaults:
    controller: <span
class="st0">&quot;projects&quot;</span>
    action: <span
class="st0">&quot;download&quot;</span>
  config:
    name: <span
class="st0">&quot;[a-z0-9]+&quot;</span>
    file: <span
class="st0">&quot;[0-9]+&quot;</span></pre>

<p>首先看第一个规则 docs_page 的比对过程：</p>

<ul>
	<li><strong>"docs_page"</strong>
		<p>首先确定 docs_page 规则需要的变量，包括
		:controller、:action、:file 和
		:name。其中前三个变量都具有默认值。</p>

		<p>比对第一个变量 :controller。这个变量在 docs_page 中指定为
		docs。而提供的参数中 :controller 的值是 projects。此时就要检查
		:controller 是否在 configs
		中指定了比对规则。如果指定了比对规则，那么 defaults 中为
		:controller 指定的就是一个“建议值”。只要 :controller
		符合比对规则也算通过。但现在 configs 并没有指定 :controller
		的规则，因此比对失败。</p>
	</li>
</ul>

<ul>
	<li><strong>"projects_show"</strong>
		<p>参考 docs_page 的比对过程，:controller 变量可以通过，但
		:action 变量却比对失败。</p>
	</li>
</ul>

<ul>
	<li><strong>"projects_download_all"</strong>
		<p>:controller、:action 变量都通过了比对。在比对 :name 时，按照
		configs 中为 :name 指定的规则，:name
		也通过了比对。接下来该路由规则已经没有更多变量了，但参数中却还有一个
		file，因此算作比对失败。</p>
	</li>

	<li><strong>"projects_download"</strong>
		<p>这个规则的四个变量和参数完全符合，因此比对通过。比对通过后，将生成
		/projects/qeephp/download/2 这样的 URL。而这个 URL
		在正向解析时又符合同一条规则，最终实现了 url() 方法生成
		URL 地址导向到正确控制器和动作的目的。</p>
	</li>
</ul>

<h2>添加路由规则</h2>

<p>在实际应用中，大多数 URL
使用默认的路由规则就足够了。少数 URL
可以进行适当的美化。例如 /tasks/edit/task_id/1 应该美化为
/tasks/edit/1。为了实现这个目的，我们修改
configs/routes.yaml，在默认规则前添加一条规则：</p>

<pre class="python
code">tasks_edit:
  pattern: /tasks/edit/:task_id
  defaults:
    controller: tasks
    action: edit
  configs:
    task_id: <span
class="st0">&quot;[0-9]+&quot;</span></pre>

<p>刷新浏览器后，再点击任务标题可以看到 URL
地址已经改变为预期的形式：</p>

<div class="figure"><img src="images/routes-rules-01.png" alt="" />
	<p>改变后的 URL 地址</p>
</div>

<p>继续增加规则：</p>

<pre class="python
code">tasks_pagination:
  pattern: /tasks/page/:page
  defaults:
    controller: tasks
    action: index
  configs:
    page: <span
class="st0">&quot;[0-9]+&quot;</span></pre>

<p>将把 /tasks/index/page/3 改写为 /tasks/page/3。</p>

<p>读者朋友们可以尝试各种写法，找到适合自己的使用方式。更复杂的基于正则的路由，请参考开发指南。</p>

<p>$Id: routes-rules.texy 2295 2009-03-10 07:48:18Z dualface $</p>

  </div>

  <div class="guide-footer">

    <table border="0" width="100%">
      <tr>
        <td align="left" width="200">
                    &laquo;
          <a href="node-routes-configs.html">启用 URL 路由</a>
          
        </td>

        <td align="center">
          本章：<a href="node-routes.html">美化 URL</a>
          <br />
          <a href="index.html">返回索引页</a>
        </td>

        <td align="right" width="200">
                    <a href="node-review.html">回顾</a> 
          &raquo;
                  </td>
      </tr>
    </table>

  </div>

</div>


</div>

</body>
</html>


